import numpy as np
import pandas as pd
from datetime import datetime
import json
from .config import FUTURES_CONFIG
import os

class ArbitrageSentinel:
    def __init__(self, std_threshold=2.0, window_sizes=[20, 60, 120]):
        """
        初始化套利哨兵
        :param std_threshold: 标准差倍数阈值，默认为2倍标准差
        :param window_sizes: 多个时间窗口
        """
        self.std_threshold = std_threshold
        self.window_sizes = window_sizes  # 多个时间窗口
        self.signals = []

    def analyze_spread(self, spread_data: pd.DataFrame, pair_name: str):
        """使用多个时间窗口分析价差"""
        self.current_data = spread_data  # 保存当前数据供其他方法使用
        spread = spread_data['收盘']
        signals = []
        
        # 首先计算全周期的统计量
        full_data = spread.copy()
        
        # 修改为2%和98%的分位数
        lower_bound = np.percentile(full_data, 2)
        upper_bound = np.percentile(full_data, 98)
        
        # 剔除异常值
        trimmed_data = full_data[(full_data >= lower_bound) & 
                                 (full_data <= upper_bound)]
        
        # 计算全周期的统计量
        full_mean = trimmed_data.mean()
        full_std = trimmed_data.std()
        current_price = spread.iloc[-1]
        
        # 计算全周期的z-score
        full_z_score = (current_price - full_mean) / full_std if full_std != 0 else 0
        
        # 添加全周期信号
        signals.append({
            'window': 'full',
            'z_score': full_z_score,
            'mean': full_mean,
            'std': full_std,
            'trimmed_mean': full_mean,
            'trimmed_std': full_std,
            'lower_bound': lower_bound,
            'upper_bound': upper_bound,
            'weight': 0.4  # 全周期权重最高
        })
        
        # 计算各个窗口的统计量
        window_weights = {
            240: 0.3,  # 长周期次高权重
            120: 0.2,  # 中周期权重
            50: 0.1    # 短周期最低权重
        }
        
        for window in self.window_sizes:
            if len(spread) < window:
                continue
            
            window_data = spread[-window:]
            lower_bound = np.percentile(window_data, 5)
            upper_bound = np.percentile(window_data, 95)
            
            trimmed_data = window_data[(window_data >= lower_bound) & 
                                     (window_data <= upper_bound)]
            
            if len(trimmed_data) < 20:
                continue
            
            current_mean = trimmed_data.mean()
            current_std = trimmed_data.std()
            
            z_score = (current_price - current_mean) / current_std if current_std != 0 else 0
            
            signals.append({
                'window': window,
                'z_score': z_score,
                'mean': current_mean,
                'std': current_std,
                'trimmed_mean': current_mean,
                'trimmed_std': current_std,
                'lower_bound': lower_bound,
                'upper_bound': upper_bound,
                'weight': window_weights.get(window, 0.1)  # 使用预设权重
            })
        
        return self._generate_composite_signal(signals, spread.iloc[-1], pair_name)

    def _generate_composite_signal(self, signals, current_spread, pair_name):
        """综合多个时间窗口的信号"""
        # 使用预设权重计算加权z-score
        weighted_z_score = sum(s['z_score'] * s['weight'] for s in signals)
        
        # 使用全周期的统计量来设置止损止盈
        full_period_stats = next(s for s in signals if s['window'] == 'full')
        std = full_period_stats['std']
        mean = full_period_stats['mean']
        
        # 计算止损止盈价位
        stop_settings = self._calculate_stop_levels(
            current_spread=current_spread,
            mean=mean,
            std=std,
            z_score=weighted_z_score
        )
        
        signal = {
            "pair_name": pair_name,
            "current_spread": float(round(current_spread, 2)),
            "signals": {
                str(s['window']) + "日" if isinstance(s['window'], int) else "全周期": {
                    "z_score": float(round(s['z_score'], 2)),
                    "mean": float(round(s['mean'], 2)),
                    "std": float(round(s['std'], 2)),
                    "weight": s['weight']
                } for s in signals
            },
            "weighted_z_score": float(round(weighted_z_score, 2)),
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "signal": "观望",
            "strength": "无",
            "action": "无",
            "stop_settings": stop_settings
        }
        
        # 调整信号阈值（可以根据需要调整）
        threshold = self.std_threshold
        if weighted_z_score <= -threshold:
            signal["signal"] = "低位"
            signal["strength"] = "强" if weighted_z_score <= -2 * threshold else "中"
            signal["action"] = "可以考虑做多价差"
        elif weighted_z_score >= threshold:
            signal["signal"] = "高位"
            signal["strength"] = "强" if weighted_z_score >= 2 * threshold else "中"
            signal["action"] = "可以考虑做空价差"
        
        return signal

    def _calculate_stop_levels(self, current_spread, mean, std, z_score):
        """计算止损止盈价位"""
        # 获取最近的价差数据
        spread = self.current_data['收盘']
        
        # 计算2%和98%的分位数
        lower_bound = np.percentile(spread, 2)
        upper_bound = np.percentile(spread, 98)
        
        # 计算价格区间和网格大小
        price_range = upper_bound - lower_bound
        grid_size = price_range / 20  # 将区间分成20个网格，使每个网格更精细
        
        if z_score <= -self.std_threshold:  # 低位信号，做多价差
            # 下跌2个网格止损，上涨1个网格止盈
            stop_loss = current_spread - (grid_size * 2)
            take_profit_1 = current_spread + grid_size  # 上涨一个网格获利
            take_profit_2 = current_spread + (grid_size * 2)  # 上涨两个网格获利
            
            # 计算风险收益比
            risk = current_spread - stop_loss
            reward1 = take_profit_1 - current_spread
            reward2 = take_profit_2 - current_spread
            
            return {
                "direction": "多头",
                "entry_price": float(round(current_spread, 2)),
                "stop_loss": float(round(stop_loss, 2)),
                "take_profit_1": float(round(take_profit_1, 2)),
                "take_profit_2": float(round(take_profit_2, 2)),
                "risk_reward_1": float(round(reward1/risk, 2)) if risk != 0 else 0,
                "risk_reward_2": float(round(reward2/risk, 2)) if risk != 0 else 0,
                "grid_size": float(round(grid_size, 2)),  # 添加网格大小信息
                "price_range": [float(round(lower_bound, 2)), float(round(upper_bound, 2))]  # 添加价格区间信息
            }
            
        elif z_score >= self.std_threshold:  # 高位信号，做空价差
            # 上涨2个网格止损，下跌1个网格止盈
            stop_loss = current_spread + (grid_size * 2)
            take_profit_1 = current_spread - grid_size  # 下跌一个网格获利
            take_profit_2 = current_spread - (grid_size * 2)  # 下跌两个网格获利
            
            # 计算风险收益比
            risk = stop_loss - current_spread
            reward1 = current_spread - take_profit_1
            reward2 = current_spread - take_profit_2
            
            return {
                "direction": "空头",
                "entry_price": float(round(current_spread, 2)),
                "stop_loss": float(round(stop_loss, 2)),
                "take_profit_1": float(round(take_profit_1, 2)),
                "take_profit_2": float(round(take_profit_2, 2)),
                "risk_reward_1": float(round(reward1/risk, 2)) if risk != 0 else 0,
                "risk_reward_2": float(round(reward2/risk, 2)) if risk != 0 else 0,
                "grid_size": float(round(grid_size, 2)),  # 添加网格大小信息
                "price_range": [float(round(lower_bound, 2)), float(round(upper_bound, 2))]  # 添加价格区间信息
            }
        
        return None

    def generate_report(self, save_path=None):
        """生成预警报告"""
        if not self.signals:
            return "没有发现任何交易信号"

        report = "期货套利哨兵系统预警报告\n"
        report += f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
        report += "=" * 50 + "\n\n"

        # 筛选出有交易信号的配对
        active_signals = [s for s in self.signals if s["signal"] != "观望"]
        
        if not active_signals:
            report += "当前没有需要特别关注的套利机会。\n"
        else:
            report += "需要关注的套利机会：\n"
            for signal in active_signals:
                report += f"\n品种对: {signal['pair_name']}\n"
                report += f"当前价差: {signal['current_spread']}\n"
                
                # 添加多周期分析结果
                report += "\n多周期分析：\n"
                for period, data in signal['signals'].items():
                    report += f"{period}： Z得分={data['z_score']:.2f}, "
                    report += f"均值={data['mean']:.2f}, "
                    report += f"标准差={data['std']:.2f}\n"
                
                report += f"\n综合Z得分: {signal['weighted_z_score']:.2f}\n"
                
                # 添加趋势信息
                if 'trend' in signal:
                    report += f"价差趋势: {signal['trend']['trend']}\n"
                    report += f"MA5: {signal['trend']['ma_values']['MA5']:.2f}\n"
                    report += f"MA20: {signal['trend']['ma_values']['MA20']:.2f}\n"
                
                # 添加季节性信息
                if 'seasonality' in signal:
                    report += f"\n当前月份均值: {signal['seasonality']['current_month_mean']:.2f}\n"
                    report += f"当前月份标准差: {signal['seasonality']['current_month_std']:.2f}\n"
                
                report += f"\n信号: {signal['signal']} ({signal['strength']})\n"
                report += f"建议操作: {signal['action']}\n"
                
                # 添加止损止盈信息
                if signal['stop_settings']:
                    report += f"\n止损止盈设置：\n"
                    stop_settings = signal['stop_settings']
                    report += f"方向: {stop_settings['direction']}\n"
                    report += f"入场价: {stop_settings['entry_price']}\n"
                    report += f"止损价: {stop_settings['stop_loss']}\n"
                    report += f"止盈价1 (均值回归): {stop_settings['take_profit_1']}\n"
                    report += f"止盈价2 (1σ): {stop_settings['take_profit_2']}\n"
                    
                    # 添加风险收益比（如果存在）
                    if 'risk_reward_1' in stop_settings and 'risk_reward_2' in stop_settings:
                        report += f"风险收益比1: 1:{stop_settings['risk_reward_1']}\n"
                        report += f"风险收益比2: 1:{stop_settings['risk_reward_2']}\n"
                
                report += "-" * 30 + "\n"

        if save_path:
            def convert_to_native_types(obj):
                """递归转换所有数值为Python原生类型"""
                if isinstance(obj, (np.int64, np.float64)):
                    return float(obj)
                elif isinstance(obj, dict):
                    return {key: convert_to_native_types(value) for key, value in obj.items()}
                elif isinstance(obj, list):
                    return [convert_to_native_types(item) for item in obj]
                return obj

            # 转换所有信号数据为Python原生类型
            json_signals = []
            for signal in self.signals:
                json_signal = convert_to_native_types(signal)
                json_signals.append(json_signal)

            # 保存JSON格式的详细数据
            with open(f"{save_path}/signals_{datetime.now().strftime('%Y%m%d')}.json", "w", 
                      encoding='utf-8') as f:
                json.dump(json_signals, f, ensure_ascii=False, indent=4)
            
            # 保存文本报告
            with open(f"{save_path}/report_{datetime.now().strftime('%Y%m%d')}.txt", "w", 
                      encoding='utf-8') as f:
                f.write(report)

        return report 

    def calculate_weighted_stats(self, spread_data, decay_factor=0.94):
        """计算带时间衰减的统计指标"""
        spread = spread_data['收盘'].values
        weights = np.array([decay_factor ** i for i in range(len(spread)-1, -1, -1)])
        weights = weights / weights.sum()  # 归一化权重
        
        weighted_mean = np.sum(spread * weights)
        weighted_std = np.sqrt(np.sum(weights * (spread - weighted_mean) ** 2))
        
        return weighted_mean, weighted_std 

    def analyze_seasonality(self, spread_data):
        """分析季节性特征"""
        df = spread_data.copy()
        df.index = pd.to_datetime(df.index)
        
        # 添加月份信息
        df['month'] = df.index.month
        
        # 计算每个月的基础统计特征
        monthly_stats = df.groupby('month')['收盘'].agg([
            'mean',          # 均值
            'std',           # 标准差
            'count',         # 样本数
            'min',           # 最小值
            'max'           # 最大值
        ]).round(2)
        
        # 单独计算偏度和峰度
        skew_values = df.groupby('month')['收盘'].apply(lambda x: x.skew()).round(2)
        kurt_values = df.groupby('month')['收盘'].apply(lambda x: x.kurt()).round(2)
        
        # 添加偏度和峰度到统计结果中
        monthly_stats['skew'] = skew_values
        monthly_stats['kurt'] = kurt_values
        
        # 计算月度变化率
        monthly_changes = df.groupby('month')['收盘'].apply(
            lambda x: x.pct_change().mean()
        ).round(4)
        
        # 计算每个月的胜率（相对于上个月的上涨概率）
        monthly_win_rates = df.groupby('month')['收盘'].apply(
            lambda x: (x.diff() > 0).mean()
        ).round(4)
        
        # 添加到结果中
        monthly_stats['change_rate'] = monthly_changes
        monthly_stats['win_rate'] = monthly_win_rates
        
        # 计算季节性强度
        seasonal_strength = monthly_stats['mean'].std() / monthly_stats['mean'].mean()
        
        return {
            'monthly_stats': monthly_stats,
            'seasonal_strength': seasonal_strength,
            'best_months': monthly_stats.nlargest(3, 'mean').index.tolist(),
            'worst_months': monthly_stats.nsmallest(3, 'mean').index.tolist()
        }

    def generate_seasonality_report(self, pair_name, seasonal_data, save_path):
        """生成季节性分析报告"""
        report = f"\n{pair_name} 季节性分析报告\n"
        report += "=" * 50 + "\n\n"
        
        # 添加季节性强度
        report += f"季节性强度: {seasonal_data['seasonal_strength']:.4f}\n"
        
        # 最佳和最差月份
        report += f"最佳交易月份: {', '.join(map(str, seasonal_data['best_months']))}\n"
        report += f"最差交易月份: {', '.join(map(str, seasonal_data['worst_months']))}\n\n"
        
        # 月度统计数据
        report += "月度统计详情：\n"
        report += seasonal_data['monthly_stats'].to_string()
        report += "\n\n"
        
        # 交易建议
        report += "交易建议：\n"
        for month in range(1, 13):
            stats = seasonal_data['monthly_stats'].loc[month]
            suggestion = "建议："
            
            if month in seasonal_data['best_months']:
                suggestion += "适合做多价差，"
            elif month in seasonal_data['worst_months']:
                suggestion += "适合做空价差，"
            else:
                suggestion += "中性，"
            
            suggestion += f"胜率 {stats['win_rate']:.2%}，"
            suggestion += f"平均变化率 {stats['change_rate']:.2%}"
            
            report += f"{month}月: {suggestion}\n"
        
        # 保存报告
        if save_path:
            report_file = os.path.join(save_path, 
                                     f"seasonal_report_{pair_name}_{datetime.now().strftime('%Y%m%d')}.txt")
            with open(report_file, 'w', encoding='utf-8') as f:
                f.write(report)
        
        return report

    def analyze_trend(self, spread_data, windows=[5, 20]):
        """分析价差趋势"""
        spread = spread_data['收盘']
        trends = {}
        
        for window in windows:
            ma = spread.rolling(window=window).mean()
            trends[f'MA{window}'] = ma.iloc[-1]
        
        # 判断趋势
        trend_signal = "上升" if trends['MA5'] > trends['MA20'] else "下降"
        
        return {
            'trend': trend_signal,
            'ma_values': trends
        } 